package com.compute.serviceImpl.ManageAlgorithm;

import com.compute.MyAnnotation.algorithm;
import com.compute.bean.FixedEvent;
import com.compute.bean.Task;
import com.compute.serviceImpl.EventManageServiceImpl;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static Tools.DateCompute.getNowMinutesByWeekNumAndMinutes;
import static Tools.DateCompute.getNowMinutesFrom20200106;

/**
 * @author 李天航
 * 活动算法
 */
@Service
public class TaskManageAlgorithm {

    @AllArgsConstructor
    public static class TaskAndVis{
        Task task;
        boolean vis;
    }

    /**
     * 短作业优先
     * @param events 填充后的活动安排表
     * @param tasks 待安排的活动
     * @return 算法结果
     */
    @algorithm
    public List<FixedEvent> littleEventsFirst(List<FixedEvent> events,List<Task> tasks) throws Exception {
        List<TaskAndVis> visTasks=new ArrayList<>();
        for(Task task1:tasks) {
            visTasks.add(new TaskAndVis(task1, false));
        }
        visTasks.sort((o1,o2)-> (int) (o1.task.getTaskTime()-o2.task.getTaskTime()));
        if(!trySearchAnswer(events, visTasks, 0, 0)){
            throw new Exception("活动安排失败");
        }
        return events;
    }
    /**
     * 长作业优先
     * @param events 填充后的活动安排表
     * @param tasks 待安排的活动
     * @return 算法结果
     */
    @algorithm
    public List<FixedEvent> bigEventsFirst(List<FixedEvent> events,List<Task> tasks) throws Exception {
        List<TaskAndVis> visTasks=new ArrayList<>();
        for(Task task1:tasks) {
            visTasks.add(new TaskAndVis(task1, false));
        }
        visTasks.sort((o1,o2)-> (int) (o2.task.getTaskTime()-o1.task.getTaskTime()));
        if(!trySearchAnswer(events, visTasks, 0, 0)){
            throw new Exception("活动安排失败");
        }
        return events;
    }
    /**
     * 按照原来的顺序的作业优先
     * @param events 填充后的活动安排表
     * @param tasks 待安排的活动
     * @return 算法结果
     */
    @algorithm
    public List<FixedEvent> rawOrderFirst(List<FixedEvent> events,List<Task> tasks) throws Exception {
        List<TaskAndVis> visTasks=new ArrayList<>();
        for(Task task1:tasks) {
            visTasks.add(new TaskAndVis(task1, false));
        }
        if(!trySearchAnswer(events, visTasks, 0, 0)){
            throw new Exception("活动安排失败");
        }
        return events;
    }
    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    static class TimeWithIndex{
        long time;
        int idx;
    }

    /**
     * 负载均衡，每天的任务尽量平均
     * @param events 填充后的活动安排表
     * @param tasks 待安排的活动
     * @return 算法结果
     */
    @algorithm
    public List<FixedEvent> loadBalanceFirst(List<FixedEvent> events,List<Task> tasks) throws Exception {
        List<TaskAndVis> visTasks=new ArrayList<>();
        for(Task task1:tasks) {
            visTasks.add(new TaskAndVis(task1, false));
        }
        ArrayList<FixedEvent>[] sevenEvents=new ArrayList[7];
        TimeWithIndex[] timeUsage=new TimeWithIndex[7];
        for (int i=0;i<7;i++) {
            sevenEvents[i]=new ArrayList<>();
            timeUsage[i]=new TimeWithIndex(0,i);
        }
        for (FixedEvent event : events) {
            long idx=event.getBeginTime()/(24*60);
            if(idx>7||idx<0) {
                throw new RuntimeException("时间超出限制");
            }
            timeUsage[(int)idx].time+=event.getEndTime()-event.getBeginTime();
            sevenEvents[(int)idx].add(event);
        }
        boolean result = tryBalanceSearch(sevenEvents, visTasks, timeUsage,0);
        if(!result) {
            throw new Exception("活动安排失败");
        }
        events=new ArrayList<>();
        for (ArrayList<FixedEvent> sevenEvent : sevenEvents) {
            events.addAll(sevenEvent);
        }
        return events;
    }

    /**
     * 负载均衡的搜索算法
     * @param sevenEvents 七天的活动
     * @param visTasks 任务列表
     * @param timeUsage 时间占用
     * @param nowIdx 当前安排的活动编号
     * @return 是否成功
     */
    public boolean tryBalanceSearch(ArrayList<FixedEvent>[] sevenEvents,List<TaskAndVis> visTasks,TimeWithIndex[] timeUsage,int nowIdx){
        if(nowIdx==visTasks.size()){
            return true;
        }
        TimeWithIndex[] sortedTimeUsage=new TimeWithIndex[7];
        System.arraycopy(timeUsage ,0,sortedTimeUsage,0,timeUsage.length);
        Arrays.sort(sortedTimeUsage,((o1, o2) -> Long.compare(o1.time, o2.time)));
        long lbound=0,rbound=0;
        //优先选择负载小的
        for(int idx=0;idx<7;idx++){
            TaskAndVis taskAndVis = visTasks.get(nowIdx);
            Task task = taskAndVis.task;
            //对应的下标
            int i=sortedTimeUsage[idx].idx;

            for(int j=0;j<=sevenEvents[i].size();j++){
                if(j==sevenEvents[i].size()) {
                    rbound=(i+1)*24*60L;
                } else {
                    rbound=sevenEvents[i].get(j).getBeginTime();
                }
                if(rbound-lbound<=task.getTaskTime()){
                    sevenEvents[i].add(j,new FixedEvent(task,lbound));
                    sortedTimeUsage[idx].time+=task.getTaskTime();
                    visTasks.get(nowIdx).vis=true;
                    if(tryBalanceSearch(sevenEvents,visTasks,sortedTimeUsage,nowIdx+1)){
                        return true;
                    }
                    visTasks.get(nowIdx).vis=true;
                    sortedTimeUsage[idx].time-=task.getTaskTime();
                    sevenEvents[i].remove(j);
                }
                if(j<sevenEvents[i].size()) {
                    lbound=sevenEvents[i].get(j).getEndTime();
                }
            }

        }
        return false;
    }
    /**
     * 尝试安排活动
     * @param alreadyIn 已经安排好的活动
     * @param tasks 待安排的活动
     * @param idx 当前在安排第几个活动
     * @param insertidx 上一次安排的活动的下标
     * @return 是否成功
     */
    public boolean trySearchAnswer(List<FixedEvent> alreadyIn, List<TaskAndVis> tasks, int idx, int insertidx)
    {
        if(idx==tasks.size()||tasks.size()==0) {
            return true;
        }
        long weekNum=tasks.get(0).task.getWeekNum();
        for(TaskAndVis taskAndVis:tasks)
        {
            if(!taskAndVis.vis)
            {
                int j=insertidx;
                while(j+1<alreadyIn.size()
                        &&(alreadyIn.get(j+1).getBeginTime()-alreadyIn.get(j).getEndTime())<taskAndVis.task.getTaskTime()
                        &&getNowMinutesByWeekNumAndMinutes(weekNum,alreadyIn.get(j).getEndTime())<getNowMinutesFrom20200106()
                ) {
                    j++;
                }
                long beginTime=alreadyIn.get(j).getEndTime();
                long endTime=beginTime+taskAndVis.task.getTaskTime();
                //结束时间超过底线
                if(endTime>=taskAndVis.task.getTaskDeadline())
                {
                    continue;
                }
                alreadyIn.add(j+1,new FixedEvent(taskAndVis.task,alreadyIn.get(j).getEndTime()));
                taskAndVis.vis=true;
                if(trySearchAnswer(alreadyIn,tasks,idx+1,j)) {
                    return true;
                }
            }
        }
        return false;
    }
}
